package demo.log.adapter;

import demo.log.model.GenericLogEvent;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 使用这种方法的好处：
 *
 * <li>编译时独立：不需要在编译时引入所有支持的日志框架。
 * <li>运行时灵活：可以根据实际环境动态适配不同的日志框架。
 * <li>易于扩展：只需更新 adapterMap 就可以添加新的日志框架支持。
 * <li>低耦合：主要逻辑不直接依赖于特定的日志框架类。
 */
public class LogEventAdapterFactory {
    private static final Map<String, String> adapterMap = new HashMap<>();
    private static final Map<String, Constructor<?>> constructorCache = new ConcurrentHashMap<>();

    static {
        // 使用完全限定类名作为键
        // log4j
        adapterMap.put("org.apache.log4j.spi.LoggingEvent", "org.demo.log.log4j.Log4jEventAdapter");
        // logback
        adapterMap.put("ch.qos.logback.classic.spi.ILoggingEvent", "org.demo.log.logback.LogbackEventAdapter");
        // log4j2
        adapterMap.put("org.apache.logging.log4j.core.LogEvent", "org.demo.log.log4j.Log4j2EventAdapter");
    }

    /**
     * 根据入参适配对应的 GenericLogEvent 对象
     *
     * @param event
     * @return
     */
    public static GenericLogEvent adaptLogEvent(Object event) {
        if (event == null) {
            return null;
        }
        String adapterClassName = findAdapterClassName(event);

        if (adapterClassName == null) {
            String eventClassName = event.getClass().getName();
            // 没有找到合适的适配器
            System.err.println("No suitable adapter found for event type: " + eventClassName);
            return null;
        }
        return createAdapter(adapterClassName, event);
    }

    /**
     * 根据logEvent类型，返回对应的适配器类名
     * @param event
     * @return
     */
    private static String findAdapterClassName(Object event) {
        Class<?> eventClass = event.getClass();
        String eventClassName = eventClass.getName();
        // 首先尝试直接匹配
        if (adapterMap.containsKey(eventClassName)) {
            return adapterMap.get(eventClassName);
        }

        // 如果没有直接匹配，尝试查找父类或接口
        for (Map.Entry<String, String> entry : adapterMap.entrySet()) {
            try {
                Class<?> knownEventClass = Class.forName(entry.getKey());
                if (knownEventClass.isAssignableFrom(eventClass)) {
                    return entry.getValue();
                }
            } catch (ClassNotFoundException e) {
                // 忽略异常，因为类可能不存在
            }
        }
        return null;
    }

    private static GenericLogEvent createAdapter(String adapterClassName, Object event) {
        try {
            Class<?> adapterClass = Class.forName(adapterClassName);
            Constructor<?> constructor = findSuitableConstructor(adapterClass, event);
            if (constructor != null) {
                return (GenericLogEvent) constructor.newInstance(event);
            } else {
                System.err.println("No suitable constructor found for " + adapterClassName + " with event type " + event.getClass().getName());
                return null;
            }
        } catch (Throwable e) {
            System.err.println("Error while trying to create adapter for event type: " + event.getClass().getName());
            return null;
        }
    }

    /**
     * 根据入参适配对应的构造函数
     *
     * @param adapterClass
     * @param event
     * @return
     */
    private static Constructor<?> findSuitableConstructor(Class<?> adapterClass, Object event) {
        String eventClassName = event.getClass().getName();
        return constructorCache.computeIfAbsent(eventClassName, k -> {
            for (Constructor<?> constructor : adapterClass.getConstructors()) {
                Class<?>[] paramTypes = constructor.getParameterTypes();
                if (paramTypes.length == 1 && paramTypes[0].isAssignableFrom(event.getClass())) {
                    return constructor;
                }
            }
            return null;
        });
    }
}


